#include"meta_stream.hpp"
#include<string>
#include<sstream>
#include<iostream>
#include<assert.h>
#include"Algo.hpp"
using namespace meta_ios;

// Ensure the number of placeholders matches the format
// This is the first example using meta streams
template<static_str str>
constexpr std::size_t count_hash = meta_all_transfer<
    // meta_filter_ostream is a meta output stream, which receives elements from the input that satisfy the condition meta function and outputs them to the end of the specified container exp_list<>
    meta_filter_ostream<exp_list<>,
    meta_fold<value_is<'#'>,
    // This is a stream transfer protocol, only_arg tells the stream that this meta function only needs the value from the input stream, not the entire stream state
    protocols::only_arg>>,
    // This is a meta character input stream
    meta_char_istream<str>
    // We can directly get the number of placeholders by getting the length of the result list
    >::to::type::length;

// This stream is used to quickly split the string by #
template<class this_list, char c = '#'>
using cut_hash = meta_all_transfer<
    // This is an output stream, which continuously puts elements from the input stream into the specified container exp_list<> at the end
    meta_ostream<exp_list<>>,
    // This is an input stream, which continuously outputs elements from the specified list until the list is empty or the stream state satisfies break_f and is stopped
    meta_istream<this_list>,
    // This is break_f, when this function returns true, the stream receives an interrupt signal and stops transmission
    meta_fold<typename value_is<c>::template OR<value_is<'\0'>>,
    // This is a stream transfer protocol, only_stream_cache_unref extracts an element from the input stream's cache and passes it to break_f, this happens before the actual transmission, so # will not be transmitted
    protocols::only_stream_cache_unref>
>;

// Custom meta input stream: requires a conversion function F and a return function Ret

// Define how the list is reduced by F
template<char c = '#'> struct hash_dec {
    template<class this_list>
    using apply = meta_fold<
        // The result of cut_hash is a stream, which has to (output stream) and from (input stream) states, here we extract the remaining part, which is the input stream from
        typename cut_hash<this_list, c>::from,
        // Since cut_hash stops when encountering #, we need to discard #, here we directly call the input stream with no arguments, the input stream is essentially a meta function, a no-argument call will pop an element
        meta_invoke,
        // Get the list from the input stream
        get_type>;
};

// Define how the input stream returns Ret
template<char c = '#'>
struct hash_ret {
    template<class this_list>
    using apply = typename meta_fold<
        // Here we get the output part to of cut_hash, then add '\0' at the end of the obtained list to complete the string
        typename cut_hash<this_list, c>::to, get_type>::template push_back<exp_char<'\0'>>;
};

struct exp_str_empty_t {
    template<class str>
    struct apply {
        static constexpr bool value = length_equal<str, 1> || str::template at<0>::value == 0;
    };
};

template<class str>
using no_space = typename meta_all_transfer<meta_filter_ostream<exp_list<>, protocols::only_arg<value_is_not<' '>>>,
    meta_istream<str>>::to::type;


// Combine the above F and Ret, use meta_ret_object to create a custom meta input stream
template<static_str s>
using hash_read_istream = meta_ret_object<
    typename str_to_list<s>::template push_back<exp_char<'#'>>,
    hash_dec<>,
    hash_ret<>
>;
// Combine the above F and Ret, use meta_ret_object to create a custom meta input stream
template<class str, char c>
using delim_read_istream = meta_ret_object<
    typename str::template push_back<exp_char<c>>,
    hash_dec<c>,
    hash_ret<c>
>;

//meta_format<"#{:/., 4...}", argc<4>>(1,2,3,4)

template<bool begin, class TL>
struct find_end {
    static constexpr bool value = false;
    static constexpr int pos = -1;
};

template<class TL>
struct find_end<true, TL> {
    static constexpr bool value = exp_try_find<exp_char<'}'>, TL>::value;
    static constexpr int pos = value ? exp_find<exp_char<'}'>, TL> ::value : -1;
    static_assert(value, "error, uncompleted format");
};

template<class TL>
struct try_find_format_parser {
    static constexpr bool value =
        find_end<exp_find<exp_char<'{'>, TL>::value == 0, TL>::value;
    static constexpr int pos = find_end<value, TL>::pos;
};

template<class TL>
struct parse_format {
    static constexpr int pos = try_find_format_parser<TL>::pos;
    using selector = typename meta_fold<typename meta_all_transfer<
        meta_ostream<exp_list<>>,
        meta_count_istream<1, pos - 1>
    >::to::type, to_meta_array_t>::template to<exp_list_select>;
    using context = typename meta_invoke<selector, TL>::template push_back<exp_char<0>>;
    using parsed = typename meta_all_transfer<
        meta_transform_ostream<exp_list<>, meta_fold<meta_quote::unary<no_space>, protocols::only_arg>>,
        delim_read_istream<context, ','>,
        protocols::only_stream_from_unref<exp_str_empty_t>
    >::to::type;
};